﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary.Space.QueryEngine
{
    /// <summary>
    /// This class represents an abstract query layer.
    /// </summary>
    [Serializable]
    public abstract class AbstractSpaceQuery
    {
        /// <summary>
        /// The maximum number of query results.
        /// </summary>
        private int limit;

        /// <summary>
        /// The object status criteria.
        /// </summary>
        private SpaceObjectStatus status;

        /// <summary>
        /// Initializes a new instance of the <see cref="AbstractSpaceQuery"/> class.
        /// </summary>
        protected AbstractSpaceQuery()
        {
            this.status = SpaceObjectStatus.Unlocked;
            this.limit = int.MaxValue;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AbstractSpaceQuery"/> class.
        /// </summary>
        /// <param name="status">The object status criteria.</param>
        protected AbstractSpaceQuery(SpaceObjectStatus status)
        {
            this.status = status;
            this.limit = int.MaxValue;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AbstractSpaceQuery"/> class.
        /// </summary>
        /// <param name="limit">The maximum number of query results.</param>
        protected AbstractSpaceQuery(int limit)
        {
            this.limit = limit;
            this.status = SpaceObjectStatus.Unlocked;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AbstractSpaceQuery"/> class.
        /// </summary>
        /// <param name="limit">The maximum number of query results.</param>
        /// <param name="status">The object status criteria.</param>
        protected AbstractSpaceQuery(int limit, SpaceObjectStatus status)
        {
            this.limit = limit;
            this.status = status;
        }

        /// <summary>
        /// Gets or sets the maximum number of query results.
        /// </summary>
        /// <value>The maximum number of query results.</value>
        public int Limit
        {
            get { return this.limit; }
            set { this.limit = value; }
        }

        /// <summary>
        /// Gets or sets the object status criteria.
        /// </summary>
        /// <value>The object status criteria.</value>
        public SpaceObjectStatus Status
        {
            get { return this.status; }
            set { this.status = value; }
        }

        /// <summary>
        /// Executes the query by using the specified parameters.
        /// </summary>
        /// <param name="items">The query source items.</param>
        /// <returns>The space ID´s, which fulfill the query criterias.</returns>
        public uint[] ExecuteQuery(ref List<SpaceItem> items)
        {
            int limitCounter = 0;
            List<uint> result = new List<uint>();

            for (int i = 0; i < items.Count; i++)
            {
                if (limitCounter < this.Limit)
                {
                    if (CheckStatus(items[i], this.Status))
                    {
                        if (this.QueryDefinition(items[i].Value))
                        {
                            result.Add(items[i].Id);
                            limitCounter++;
                        }
                    }
                }
            }

            return result.ToArray();
        }

        /// <summary>
        /// Defines the query criteria of the specified query.
        /// </summary>
        /// <param name="item">The query source items.</param>
        /// <returns>The space ID fulfills the query criteria the method returns true otherwise, false.</returns>
        public abstract bool QueryDefinition(ISpaceEntry item);

        private static bool CheckStatus(SpaceItem item, SpaceObjectStatus objectStatus)
        {
            switch (objectStatus)
            {
                case SpaceObjectStatus.Locked:

                    if (item.Locked)
                    {
                        return true;
                    }

                    return false;

                case SpaceObjectStatus.Unlocked:

                    if (item.Locked == false)
                    {
                        return true;
                    }

                    return false;

                default:
                    return true;
            }
        }
    }
}